package main.java.com.lee.juc;

import main.java.com.lee.Util;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;

/**
 * @Description TODO
 * @Author winston
 * @DateTime 2022/1/19
 */
public class MyCLHLock implements Lock {

    // 当前节点的线程本地变量
    private ThreadLocal<Node> curNodeLocal = new ThreadLocal<>();
    // 队尾指针
    private AtomicReference<Node> tail = new AtomicReference<>(null);

    // 默认队尾指针为Empty节点
    public MyCLHLock(){
        tail.getAndSet(Node.EMPTY);
    }


    /**
     * 上锁
     */
    @Override
    public void lock() {
        // 初始化一个node, 代表当前线程
        Node curNode = new Node(true, null);
        // 暂存尾节点
        Node preNode = tail.get();

        // 自旋操作: 将当前节点插入队尾.
        while (!tail.compareAndSet(preNode, curNode)) {
            // cas失败, 重新获取尾节点
            preNode = tail.get();
        }

        // 插入队尾执行成功,tail被设置为 当前线程节点,  设置当前节点的前驱结点为上一个暂存的尾结点.
        curNode.setPrevNode(preNode);

        // 自旋监听前驱节点, 监听locked变量, 直到为false.
        // 如果前驱节点的locked为true, 表示前驱节点处于抢占中, 或者持有锁过程中
        while (curNode.getPrevNode().isLocked()) {
            // 让出cpu时间片, 提供性能
            Thread.yield();
        }

        // 加锁成功,
        Util.print(Thread.currentThread().getName() + "加锁成功.");
        // 把当前节点缓存在线程本地变量中.
        curNodeLocal.set(curNode);
    }

    @Override
    public void lockInterruptibly() throws InterruptedException {

    }

    @Override
    public boolean tryLock() {
        return false;
    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public void unlock() {
        // 从线程本地变量中获取当前线程节点
        Node curNode = curNodeLocal.get();
        // 设置加锁标识false, 方便其他线程加锁
        curNode.setLocked(false);
        // 设置前驱节点为null, help gc
        curNode.setPrevNode(null);
        // 清空当前线程本地变量.
        curNodeLocal.remove();
    }

    @Override
    public Condition newCondition() {
        return null;
    }

    static class Node{

        volatile boolean locked;

        Node prevNode;

        public boolean isLocked() {
            return locked;
        }

        public void setLocked(boolean locked) {
            this.locked = locked;
        }

        public Node getPrevNode() {
            return prevNode;
        }

        public void setPrevNode(Node prevNode) {
            this.prevNode = prevNode;
        }

        public Node(boolean locked, Node prevNode) {
            this.locked = locked;
            this.prevNode = prevNode;
        }



        public static final Node EMPTY = new Node(false, null);
    }
}
